Skip to content

fix(snowflake/ast): generated walker skipped all non-Node helper-struct fields (SELECT targets, CTEs, CASE whens, OVER, MERGE whens, ...)#294

Merged
h3n4l merged 1 commit into
mainfrom
fix/snowflake/walker-coverage
Jun 10, 2026
Merged

fix(snowflake/ast): generated walker skipped all non-Node helper-struct fields (SELECT targets, CTEs, CASE whens, OVER, MERGE whens, ...)#294
h3n4l merged 1 commit into
mainfrom
fix/snowflake/walker-coverage

Conversation

@h3n4l

@h3n4l h3n4l commented Jun 10, 2026

Copy link
Copy Markdown
Member

Fix

snowflake/ast generated walker silently skipped every Node-bearing field whose type is a non-Node helper structast.Walk/Inspect never reached them. The audit (throwaway AST analyzer, cross-checked against the generated file) found this covered far more than the reported symptom: SelectStmt.Targets (the whole SELECT list), SelectStmt.With (CTE bodies), CaseExpr.Whens, FuncCallExpr.Over/OrderBy, MergeStmt.Whens, UpdateStmt.Sets, CreateTableStmt.Columns/Constraints, AlterTable actions, and ~30 statements' Options/Tags lists — 25 node-bearing helper structs total.

genwalker change

Full shape classifier + fixpoint computation of node-bearing helpers (handles nesting WindowSpec→WindowFrame→WindowBound); emits one nil-safe walk<Helper> per helper; fatal guard — any node-bearing field in an unrecognized shape now fails generation instead of silently skipping (prevents recurrence). Output: 141→148 cases, 287→447 child fields, 25 helper walkers; deterministic; gofmt-clean.

Found by

Codex cross-review of the bytebase snowflake cutover (CASE/window lineage came back empty in the migrated query-span extractor). Same bug class as the googlesql CompareExpr walk gap (#274).

Tests

walk_coverage_test.go (31 subtests on real SQL — all fail on the old walker) + 3 analysis/query_span tests proving CASE/window/WITHIN-GROUP columns are now attributed (previously lost). Full go test ./snowflake/... -count=1 green incl. corpus; deparse unaffected. Orchestrator independently verified on a fresh checkout of 1f14ee92: build/vet/gofmt clean, regen byte-identical, full suite green.

Cross-engine audit (report only, this PR fixes snowflake)

  • mysql + tidb AFFECTED: [][]ExprNode never walked → InsertStmt.Values / ValuesStmt.Rows invisible to Walk/Inspect.
  • pg + redshift AFFECTED: by-value struct fields skipped → CreateForeignTableStmt.Base CreateStmt unreachable.
  • googlesql / oracle / mssql / trino / doris clean. No other generator has the fatal guard — recommend porting it with those fixes.

🤖 Generated with Claude Code

…xpr.Whens, FuncCallExpr.Over/OrderBy, ...) — genwalker recursion

The generated walker only handled Node / []Node / [][]Node / *NodeStruct
fields, silently skipping every other node-bearing shape. ast.Inspect/Walk
therefore never reached:

- non-Node helper structs (CaseExpr.Whens []*WhenClause, FuncCallExpr.Over
  *WindowSpec, FuncCallExpr.OrderBy []*OrderItem, SelectStmt.With []*CTE /
  Targets []*SelectTarget / GroupBy / OrderBy / Fetch, UpdateStmt.Sets,
  MergeStmt.Whens, InsertMultiStmt.Branches, SetStmt.Vars,
  JsonLiteralExpr.Pairs, TableConstraint.References, ColumnDef helpers,
  CloneSource, ViewColumn, RowAccessPolicy, AlterTableAction, ...)
- slices of pointers to Node structs ([]*ColumnDef, []*ObjectName,
  []*CopyOption, []*TypeName, []*PivotValue, []*UnpivotColumn, ...)
- by-value Node structs (FuncCallExpr.Name ObjectName)

This broke column collection in every walker consumer — analysis query-span
lost CASE WHEN/THEN and OVER (PARTITION BY / ORDER BY) columns, and the
advisor never saw CTE bodies or SELECT-list subqueries. Same bug class as
the googlesql CompareExpr walk gap (#274).

genwalker now classifies all node-bearing field shapes, emits one walk<T>
function per node-bearing non-Node helper struct (fixpoint over nested
helpers, e.g. WindowSpec -> WindowFrame -> WindowBound), walks []*T node
slices and by-value Node fields, and fails generation loudly if a
node-bearing field has an unsupported shape so the gap cannot silently
reopen. Output stays deterministic and gofmt-clean (148 cases, 447 child
fields, 25 helper walkers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@h3n4l h3n4l merged commit 9756290 into main Jun 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant